<?php
/**
 * Router
 *
 *
 * @package boot
 * @author  Platform Team
 */

class Router
{
    const ERROR_INIT_FAIL       = 1;
    const ERROR_NOT_FOUND_MAP   = 2;

    const DELIM_FILTER = '::';

    /**
     * 인스턴스
     * @var Router
     */
    private static $instance;

    /**
     * Sslwrap 객체
     * @var Sslwrap
     */
    private static $Sslwrap;

    /**
     * 라우팅 맵 객체
     * @var coreRouterMap[Map명]
     */
    private static $Map;

    /**
     * 파라미터 관리 객체
     * @var coreRouterParamNormal
     */
    private static $Param;

    /**
     * 파라미터 필터 동작 여부
     * @var boolean
     */
    private static $isFilter;

    /**
     * 동작 여부
     * @var boolean
     */
    private static $isProgress;

    /**
     * CGI 환경에서의 PATH_INFO 할당 문제 수정 여부
     * @var boolean
     */
    private static $isFixCgiPathinfo;

    /**
     * 정의형 필터링 규칙
     * @var array
     */
    private static $aFilter;

    /**
     * 정의형 라우팅 규칙
     * @var array
     */
    private static $aMapper;

    /**
     * 라우팅 타입
     * @var string
     */
    private static $sType;

    /**
     * run 에러 메세지
     * @var string | false
     */
    private static $sErrorMsg;

    /**
     * run 에러 코드
     * @var string
     */
    private static $sErrorCode;

    /**
     * 생성자
     */
    private function __construct() {}

    /**
     * singleton
     *
     * @return Router
     */
    public static function getInstance()
    {
        if (!self::$instance) self::$instance = new self;

        return self::$instance;
    }

    /**
     * r alias 함수에서 사용하기 위해 만든 메소드
     *
     * @return Router
     */
    public static function instance()
    {
        return self::$instance;
    }

    /**
     * 인스턴스 파괴
     */
    public static function instancedel()
    {
        self::$Sslwrap = null;
        self::$Map = null;
        self::$isFilter = null;
        self::$isProgress = null;
        self::$isFixCgiPathinfo = null;
        self::$aFilter = null;
        self::$aMapper = null;
        self::$sType = null;
        self::$sErrorCode = null;
        self::$sErrorMsg = null;

        self::$instance = null;
    }

    /**
     * 초기화
     *
     * @param string  $sType                라우팅 타입
     * @param boolean $isFilter             필터 사용 여부 (기본 : false)
     * @param boolean $isFixCgiPathinfo     CGI 환경에서의 PATH_INFO 할당 문제 수정 여부 (기본 : false)
     * @param Sslwrap $Sslwrap              Sslwrap 객체
     */
    public function initialize($sType, $isFilter=false, $isFixCgiPathinfo=false, $Sslwrap=null)
    {
        self::$Sslwrap          = $Sslwrap;
        self::$sType            = $sType;
        self::$isFilter         = ($isFilter !== true) ? false : true;
        self::$isFixCgiPathinfo = ($isFixCgiPathinfo !== true) ? false : true;
        self::$isProgress       = true;

        self::runMap();
        self::runFilter();
        self::runParam();

        if (!self::$isProgress) {
            Exceptioner::run(FW_EXCEPTION_ROUTER, self::$sErrorMsg, self::$sErrorCode);
            throw new Exception('Router initialize failed - ' . self::$sErrorMsg, E_ERROR);
        }

        return true;
    }

    /**
     * 업로드 파일이 존재하는 지 파악
     *
     * @return boolean
     */
    public function isUploaded()
    {
        return self::$Param->isUploaded();
    }

    /**
     * 파라미터가 존재하는지 파악
     *
     * @return boolean
     */
    public function isParam($sKey, $sType=null)
    {
        return self::$Param->isParam($sKey, $sType);
    }

    /**
     * 파라미터 전체를 반환
     *
     * @return array
     */
    public function getParams()
    {
        return self::$Param->getParams();
    }

    /**
     * 원본 파라미터 전체를 반환
     *
     * @return array
     */
    public function getParams_()
    {
        return self::$Param->getParams_();
    }

    /**
     * 파라미터 반환
     * (인자를 부여하지 않을 경우는 request 키에 해당되는 파라미터 전체를 반환)
     *
     * @param string $sKey
     * @param string $sType ( 기본은 request - (get, post, files, request) )
     * @return mixed
     */
    public function getParam($sKey=null, $sType=null)
    {
        return self::$Param->getParam($sKey, $sType);
    }

    /**
     * 필터링이 적용안된 원본 파라미터 반환
     * (인자를 부여하지 않을 경우는 request 키에 해당되는 파라미터 전체를 반환)
     *
     * @param string $sKey
     * @param string $sType ( 기본은 request - (get, post, files, request) )
     * @return mixed
     */
    public function getParam_($sKey=null, $sType=null)
    {
        return self::$Param->getParam_($sKey, $sType);
    }

    /**
     * 컨트롤러명을 반환합니다.
     *
     * @return string
     */
    public function getController()
    {
        return self::$Map->getController();
    }

    /**
     * 컨트롤러 경로를 반환합니다.
     *
     * @return string
     */
    public function getControllerPath()
    {
        return self::$Map->getControllerPath();
    }

    /**
     * 메소드명을 반환합니다.
     *
     * @return string
     */
    public function getMethod()
    {
        return self::$Map->getMethod();
    }

    /**
     * 메소드에 전달될 파라미터 배열을 반환합니다.
     *
     * @return array | false
     */
    public function getMethodParam()
    {
        return self::$Map->getMethodParam();
    }

    /**
     * 지정된 파라미터를 할당합니다.
     *
     * @param string $sKey
     * @param mixed $mVal
     * @param string $sType (get | post | request) null인 경우는 3개 타입에 모두 할당
     * @return boolean
     */
    public function setParam($sKey, $mVal, $sType=null)
    {
        return self::$Param->setParam($sKey, $mVal, $sType);
    }

    /**
     * 전체 파라미터에 새로운 파라미터로 대체합니다.
     *
     * @param array $aParam
     */
    public function overrideParams($aParam)
    {
        self::$Param->overrideParams($aParam);
    }

    /**
     * 암호화 - 백엔드
     * 비활성화시 원본데이터 반환
     *
     * @param mixed  $mData    암호화시킬 데이터
     * @param string $sType    타입(로그용 문자열)
     * @return utilSslwrapException | string
     */
    public function encryptBackend($mData, $sType)
    {
        return self::$Sslwrap->encryptBackend($mData, $sType);
    }

    /**
     * 암호화 - 프론트엔드
     * 비활성화시 원본데이터 반환
     *
     * @param mixed $mData     암호화시킬 데이터
     * @return utilSslwrapException | string
     */
    public function encryptFrontend($mData)
    {
        return self::$Sslwrap->encryptFrontend($mData);
    }

    /**
     * 복호화 - 백엔드
     * 비활성화시 원본데이터 반환
     *
     * @param string $sData     복호화시킬 암호화 문자열
     * @param string $sType     타입(로그용 문자열)
     * @param string $sFromIp   요청한 서버 아이피(API내부적으로 유효성 검증에 사용)
     * @return utilSslwrapException | mixed
     */
    public function decryptBackend($sData, $sType, $sFromIp=null)
    {
        return self::$Sslwrap->decryptBackend($sData, $sType, $sFromIp);
    }

    /**
     * 복호화 - 프론트엔드
     * 비활성화시 원본데이터 반환
     *
     * @param string $sData       복호화시킬 암호화 문자열
     * @return utilSslwrapException | mixed
     */
    public function decryptFrontend($sData)
    {
        return self::$Sslwrap->decryptFrontend($sData);
    }

    /**
     * 라우팅 맵 규칙 추가  - 세그먼트 방식일 경우 유효
     *
     * @param string $sMatch   매칭식
     * @param array $aMap     재작성 정의 배열
     */
    public static function mapper($sMatch, $aMap)
    {
        self::$aMapper[$sMatch] = $aMap;
    }

    /**
     * XSS 필터링 규칙 추가
     *
     * @param string $sKey     컨트롤러 패쓰 (예 : contAdminLogin 컨트롤러라면 admin/login)
     * @param boolean $isUse    필터 사용여부 (기본 : true)
     */
    public static function filter($sKey, $isUse=true)
    {
        $sKey = trim($sKey);

        if (empty($sKey)) return;

        $sKey = strtolower($sKey);
        $sKey = preg_replace('@(^[/]+|[/]+$)@', '', $sKey);
        $isUse = $isUse !== false ? true : true;

        self::$aFilter[$sKey] = $isUse;
    }

    /**
     * 매핑 처리
     */
    private static function runMap()
    {
        if (!self::$isProgress) return;

        $sClass = FW_CLASS_PREFIX_FRAMEWORK_CORE . 'RouterMap' . ucfirst(strtolower(self::$sType));

        if ( !class_exists($sClass) ) {
            self::$isProgress = false;
            self::$sErrorMsg = 'Router Mapping Class is not found';
            self::$sErrorCode = self::ERROR_NOT_FOUND_MAP;
            return;
        }

        self::$Map = new $sClass(self::$aMapper, self::$isFixCgiPathinfo);

        self::$Map->parse();

        $sMsg = self::$Map->getErrorMsg();

        if ($sMsg !== false) {
            self::$isProgress = false;
            self::$sErrorMsg = $sMsg;
            self::$sErrorCode = self::ERROR_NOT_FOUND_MAP;
            return;
        }
    }

    /**
     * 특정 컨트롤러 메소드의 필터 사용여부 체크
     */
    private static function runFilter()
    {
        if (!self::$isProgress) return;

        $sContPath = strtolower(self::getControllerPath());
        $sMethod = strtolower(self::getMethod());

        $sKey = $sContPath . self::DELIM_FILTER . $sMethod;

        if (!isset(self::$aFilter[$sKey])) {

            if (!isset(self::$aFilter[$sContPath])) {
                return;
            } else {
                $sKey = $sContPath;
            }
        }

        if (self::$aFilter[$sKey] === true) {
            self::$isFilter = true;
        } else {
            self::$isFilter = false;
        }
    }

    /**
     * 파라미터 처리
     */
    private static function runParam()
    {
        if (!self::$isProgress) return;

        $sClass = FW_CLASS_PREFIX_FRAMEWORK_CORE . 'RouterParam';

        if (self::$isFilter === true) {
            $sClass .= 'Filter';
        } else {
            $sClass .= 'Normal';
        }

        self::$Param = new $sClass(self::$Sslwrap);

        self::$Param->parse();
    }
}
